﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using System.Xml.Linq;
using Microsoft.Xna.Framework;

namespace Pavilion
{
    public class GameItem
    {
        private Dictionary<Type, GameToolBase> gameToolDictionary;
        private bool isEnabled;

        public DrawVisibilityGroup DrawVisibilityGroup { get; private set; }
        public string Name { get; private set; }

        public event Action<GameTime> UpdateLogic;
        public event Action Disposed;
        public event Action<GameItem> Loaded;
        public event Action<bool> EnabledChanged;

        public GameItem(string itemName, string sessionName)
        {
            this.Name = itemName;
            this.IsEnabled = true;
            
            DrawVisibilityGroup = new DrawVisibilityGroup();
            gameToolDictionary = new Dictionary<Type, GameToolBase>();

            XElement scriptNode = XDocument.Load(GameEngine.GameSessionsDirectory + sessionName + @"\GameItems\" + itemName + ".xml").Element("script");

            LoadXmlToolProperties(scriptNode);
        }

        public void Dipose()
        {
            gameToolDictionary.Clear();

            OnDipsosed();
        }

        internal void Load()
        {
            OnLoaded();
        }

        internal void Update(GameTime gameTime)
        {
            if (IsEnabled) { OnUpdateLogic(gameTime); }
        }

        public T ObtainTool<T>() where T : GameToolBase
        {
            return gameToolDictionary[typeof(T)] as T;
        }

        public GameToolBase ObtainTool(string toolName)
        {
            IEnumerable<Type> typeCollection = gameToolDictionary.Keys.Where((type) => type.Name == toolName);

            if (typeCollection == null)
                return null;
            else
                return gameToolDictionary[typeCollection.First()];
        }

        public bool ContainsTool<T>() where T : GameToolBase
        {
            return gameToolDictionary.ContainsKey(typeof(T));
        }

        private void LoadXmlToolProperties(XElement scriptNode)
        {
            IEnumerable<XElement> toolPropertiesList = scriptNode.Element("GameTools").Elements("Tool");
            string assemblyName = scriptNode.Element("Properties").Attribute("Assembly").Value;

            foreach (XElement toolPropertiesNode in toolPropertiesList)
                CreateTool(toolPropertiesNode.Attribute("Name").Value, assemblyName, toolPropertiesNode.Element("Properties"));
        }

        private void CreateTool(string toolName, string assemblyName, XElement properties)
        {
            GameToolBase createdTool = GameEngine.CreateInstance<GameToolBase>(assemblyName, toolName);
            Type toolType = createdTool.GetType();

            while (toolType != typeof(GameToolBase))
            {
                gameToolDictionary.Add(toolType, createdTool);
                toolType = toolType.BaseType;
            }

            createdTool.Initialize(this);
            createdTool.Deserialize(properties);
        }

        public bool IsEnabled
        {
            get { return isEnabled; }
            set
            {
                if (isEnabled == value)
                    return;

                isEnabled = value;
                OnEnabledChanged();
            }
        }

        public bool IsVisible
        {
            get { return DrawVisibilityGroup.IsVisible; }
            set { DrawVisibilityGroup.IsVisible = value; }
        }

        private void OnEnabledChanged()
        {
            if (EnabledChanged != null)
                EnabledChanged(IsEnabled);
        }

        private void OnLoaded()
        {
            if (Loaded != null)
                Loaded(this);
        }

        private void OnDipsosed()
        {
            if (Disposed != null)
                Disposed();
        }

        private void OnUpdateLogic(GameTime gameTime)
        {
            if (UpdateLogic != null) { UpdateLogic(gameTime); }
        }
    }
}
